use std::{
    collections::HashMap,
    fmt::{self, Display, Formatter},
    hash::{DefaultHasher, Hash, Hasher},
};

use glass::{ContainerSerde, FieldSerde, Reflect, TypeInfo, TypeKind, VariantSerde};
use heck::{
    ToKebabCase, ToLowerCamelCase, ToPascalCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase,
};
use serde::{Deserialize, Serialize};

pub type ApiTypeId = String;

pub trait IntoApiTypeId {
    fn into_api_type_id(self) -> ApiTypeId;
}
pub trait GetApiTypeId {
    fn get_api_type_id() -> ApiTypeId;
}

impl<T: Reflect> GetApiTypeId for T {
    fn get_api_type_id() -> ApiTypeId {
        let type_id = std::any::TypeId::of::<T>();
        type_id.into_api_type_id()
    }
}

impl IntoApiTypeId for std::any::TypeId {
    fn into_api_type_id(self) -> ApiTypeId {
        let mut hasher = DefaultHasher::new();
        self.hash(&mut hasher);
        hasher.finish().to_string()
    }
}

impl IntoApiTypeId for &TypeInfo {
    fn into_api_type_id(self) -> ApiTypeId {
        self.type_id.into_api_type_id()
    }
}

#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub enum ApiTypeKind {
    Unknown,
    Null,
    UnitStruct(Vec<ApiTypeId>),
    NamedStruct(Vec<ApiTypeId>),
    TupleStruct(Vec<ApiTypeId>),
    Enum(Vec<ApiTypeId>),
    Bool,
    I8,
    U8,
    I16,
    U16,
    I32,
    Isize,
    U32,
    Usize,
    I64,
    U64,
    I128,
    U128,
    F32,
    F64,
    String,
    File,
    Option(ApiTypeId),
    Result(ApiTypeId, ApiTypeId),
    Array(ApiTypeId, usize),
    Tuple(Vec<ApiTypeId>),
    Vec(ApiTypeId),
    Set(ApiTypeId),
    Map(ApiTypeId, ApiTypeId),
}

impl From<TypeKind> for ApiTypeKind {
    fn from(value: TypeKind) -> Self {
        match value {
            TypeKind::Unknown => Self::Unknown,
            TypeKind::UnitStruct(items) => Self::UnitStruct(
                items
                    .iter()
                    .map(|t| t().type_id.into_api_type_id())
                    .collect(),
            ),
            TypeKind::NamedStruct(items) => Self::NamedStruct(
                items
                    .iter()
                    .map(|t| t().type_id.into_api_type_id())
                    .collect(),
            ),
            TypeKind::TupleStruct(items) => Self::TupleStruct(
                items
                    .iter()
                    .map(|t| t().type_id.into_api_type_id())
                    .collect(),
            ),
            TypeKind::Enum(items) => Self::Enum(
                items
                    .iter()
                    .map(|t| t().type_id.into_api_type_id())
                    .collect(),
            ),
            TypeKind::Bool => Self::Bool,
            TypeKind::I8 => Self::I8,
            TypeKind::U8 => Self::U8,
            TypeKind::I16 => Self::I16,
            TypeKind::U16 => Self::U16,
            TypeKind::I32 => Self::I32,
            TypeKind::Isize => Self::Isize,
            TypeKind::U32 => Self::U32,
            TypeKind::Usize => Self::Usize,
            TypeKind::I64 => Self::I64,
            TypeKind::U64 => Self::U64,
            TypeKind::I128 => Self::I128,
            TypeKind::U128 => Self::U128,
            TypeKind::F32 => Self::F32,
            TypeKind::F64 => Self::F64,
            TypeKind::String => Self::String,
            TypeKind::File => Self::File,
            TypeKind::Option(t) => Self::Option(t().type_id.into_api_type_id()),
            TypeKind::Result(t, e) => Self::Result(
                t().type_id.into_api_type_id(),
                e().type_id.into_api_type_id(),
            ),
            TypeKind::Array(t, size) => Self::Array(t().type_id.into_api_type_id(), size),
            TypeKind::Tuple(items) => Self::Tuple(
                items
                    .iter()
                    .map(|t| t().type_id.into_api_type_id())
                    .collect(),
            ),
            TypeKind::Vec(t) => Self::Vec(t().type_id.into_api_type_id()),
            TypeKind::Set(t) => Self::Set(t().type_id.into_api_type_id()),
            TypeKind::Map(k, v) => Self::Map(
                k().type_id.into_api_type_id(),
                v().type_id.into_api_type_id(),
            ),
            _ => Self::Unknown,
        }
    }
}

impl Display for ApiTypeKind {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            ApiTypeKind::UnitStruct(_) => write!(f, "UnitStruct"),
            ApiTypeKind::NamedStruct(_) => write!(f, "NamedStruct"),
            ApiTypeKind::TupleStruct(_) => write!(f, "TupleStruct"),
            ApiTypeKind::Enum(_) => write!(f, "Enum"),
            ApiTypeKind::Bool => write!(f, "Bool"),
            ApiTypeKind::I8 => write!(f, "I8"),
            ApiTypeKind::U8 => write!(f, "U8"),
            ApiTypeKind::I16 => write!(f, "I16"),
            ApiTypeKind::U16 => write!(f, "U16"),
            ApiTypeKind::I32 => write!(f, "I32"),
            ApiTypeKind::Isize => write!(f, "Isize"),
            ApiTypeKind::U32 => write!(f, "U32"),
            ApiTypeKind::Usize => write!(f, "Usize"),
            ApiTypeKind::I64 => write!(f, "I64"),
            ApiTypeKind::U64 => write!(f, "U64"),
            ApiTypeKind::I128 => write!(f, "I128"),
            ApiTypeKind::U128 => write!(f, "U128"),
            ApiTypeKind::F32 => write!(f, "F32"),
            ApiTypeKind::F64 => write!(f, "F64"),
            ApiTypeKind::String => write!(f, "String"),
            ApiTypeKind::Option(_) => write!(f, "Option"),
            ApiTypeKind::File => write!(f, "File"),
            ApiTypeKind::Result(_, _) => write!(f, "Result"),
            ApiTypeKind::Array(_, _) => write!(f, "Array"),
            ApiTypeKind::Tuple(_) => write!(f, "Tuple"),
            ApiTypeKind::Vec(_) => write!(f, "Vec"),
            ApiTypeKind::Set(_) => write!(f, "Set"),
            ApiTypeKind::Map(_, _) => write!(f, "Map"),
            ApiTypeKind::Unknown => write!(f, "Unknown"),
            ApiTypeKind::Null => write!(f, "Null"),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum ApiConstraitType {
    Length(usize, usize, Option<String>),
    Range(String, String, Option<String>),
    Constant(String, Option<String>),
    Regex(String, Option<String>),
    Enumer(ApiTypeId, Option<String>),
    SelfValidate(Option<String>),
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ApiSerde {
    pub serialize: Option<String>,
    pub deserialize: Option<String>,
    pub default: bool,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ApiField {
    pub docs: Option<String>,
    pub index: usize,
    pub ident: Option<String>,
    pub name: Option<String>,
    pub type_id: ApiTypeId,
    pub attrs: Vec<String>,
    pub serde: ApiSerde,
    pub constraints: Vec<ApiConstraitType>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ApiFields {
    pub fields: Vec<ApiField>,
    pub named: bool,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ApiVariant {
    pub docs: Option<String>,
    pub index: usize,
    pub ident: String,
    pub fields: ApiFields,
    pub attrs: Vec<String>,
    pub value: Option<isize>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum ApiMembers {
    Fields(ApiFields),
    Variants(Vec<ApiVariant>),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ApiTypeInfo {
    pub type_id: ApiTypeId,
    pub type_name: String,
    pub docs: Option<String>,
    pub kind: ApiTypeKind,
    pub ident: String,
    pub attrs: Vec<String>,
    pub members: ApiMembers,
    pub enumer: bool,
}

pub trait CollectApiTypeInfo {
    fn collect(self, types: &mut HashMap<ApiTypeId, ApiTypeInfo>);
}

pub fn get_serde(
    field_ident: &Option<String>,
    field_serde: FieldSerde,
    variant_serde: Option<&VariantSerde>,
    container_serde: &ContainerSerde,
) -> ApiSerde {
    let mut serde = ApiSerde {
        serialize: None,
        deserialize: None,
        default: false,
    };
    if field_serde.skip {
        return serde;
    }
    let Some(field_ident) = field_ident else {
        return serde;
    };
    if let Some(variant_serde) = variant_serde {
        if let Some(rule) = &variant_serde.rename_serialize {
            serde.serialize = Some(transform_serde(field_ident, rule));
        }
        if let Some(rule) = &variant_serde.rename_deserialize {
            serde.deserialize = Some(transform_serde(field_ident, rule));
        }
        if let Some(rule) = &variant_serde.rename_all {
            serde.serialize = Some(transform_serde(field_ident, rule));
            serde.deserialize = Some(transform_serde(field_ident, rule));
        }
    }
    if let Some(rule) = &container_serde.rename_serialize {
        serde.serialize = Some(transform_serde(field_ident, rule));
    }
    if let Some(rule) = &container_serde.rename_deserialize {
        serde.deserialize = Some(transform_serde(field_ident, rule));
    }
    if let Some(rule) = &container_serde.rename_all {
        serde.serialize = Some(transform_serde(field_ident, rule));
        serde.deserialize = Some(transform_serde(field_ident, rule));
    }
    if field_serde.default {
        serde.default = true;
    }
    if field_serde.skip_deserialize {
        serde.deserialize = None;
    }
    if field_serde.skip_serialize {
        serde.serialize = None;
    }
    if let Some(deserialize) = field_serde.rename_deserialize {
        serde.deserialize = Some(deserialize);
    }
    if let Some(serialize) = field_serde.rename_serialize {
        serde.serialize = Some(serialize);
    }
    if serde.serialize.is_none() {
        serde.serialize = Some(field_ident.to_string());
    }
    if serde.deserialize.is_none() {
        serde.deserialize = Some(field_ident.to_string());
    }
    serde
}

pub fn transform_serde(field_ident: &String, rule: &str) -> String {
    if rule.eq_ignore_ascii_case("lowercase") {
        return field_ident.to_lowercase();
    }
    if rule.eq_ignore_ascii_case("UPPERCASE") {
        return field_ident.to_uppercase();
    }
    if rule.eq_ignore_ascii_case("PascalCase") {
        return field_ident.to_pascal_case();
    }
    if rule.eq_ignore_ascii_case("camelCase") {
        return field_ident.to_lower_camel_case();
    }
    if rule.eq_ignore_ascii_case("snake_case") {
        return field_ident.to_snake_case();
    }
    if rule.eq_ignore_ascii_case("SCREAMING_SNAKE_CASE") {
        return field_ident.to_shouty_snake_case();
    }
    if rule.eq_ignore_ascii_case("kebab-case") {
        return field_ident.to_kebab_case();
    }
    if rule.eq_ignore_ascii_case("SCREAMING-KEBAB-CASE") {
        return field_ident.to_shouty_kebab_case();
    }
    field_ident.to_string()
}

impl CollectApiTypeInfo for TypeInfo {
    fn collect(self, types: &mut HashMap<ApiTypeId, ApiTypeInfo>) {
        let type_id = self.type_id.into_api_type_id();
        if types.contains_key(&type_id) {
            return;
        }
        types.insert(
            type_id.clone(),
            ApiTypeInfo {
                type_id: ApiTypeId::default(),
                type_name: String::default(),
                docs: Some(String::default()),
                kind: ApiTypeKind::Null,
                ident: String::default(),
                attrs: vec![],
                members: ApiMembers::Fields(ApiFields {
                    fields: vec![],
                    named: false,
                }),
                enumer: false,
            },
        );

        let mut field_type_infos = vec![];
        let container_serde = self.serde;
        let mut enumer = false;
        let api_members = match self.members {
            glass::Members::Fields(fields) => {
                let mut api_fields = ApiFields {
                    fields: vec![],
                    named: fields.named,
                };
                for field in fields.fields {
                    field_type_infos.push((field.type_info_fn)());

                    let api_field = ApiField {
                        docs: field.docs,
                        index: field.index,
                        name: field.name,
                        serde: get_serde(&field.ident, field.serde, None, &container_serde),
                        ident: field.ident,
                        type_id: (field.type_info_fn)().into_api_type_id(),
                        attrs: field.attrs,
                        constraints: get_constraints(field.constraits, &mut field_type_infos),
                    };
                    api_fields.fields.push(api_field);
                }
                ApiMembers::Fields(api_fields)
            }
            glass::Members::Variants(variants) => {
                enumer = true;
                let mut api_variants = vec![];
                for variant in variants {
                    let mut api_fields = vec![];
                    let named = variant.fields.named;
                    for field in variant.fields.fields {
                        field_type_infos.push((field.type_info_fn)());

                        let api_field = ApiField {
                            docs: field.docs,
                            index: field.index,
                            serde: get_serde(
                                &field.ident,
                                field.serde,
                                Some(&variant.serde),
                                &container_serde,
                            ),
                            ident: field.ident,
                            name: field.name,
                            type_id: (field.type_info_fn)().into_api_type_id(),
                            attrs: field.attrs,
                            constraints: get_constraints(field.constraits, &mut field_type_infos),
                        };
                        api_fields.push(api_field);
                    }
                    api_variants.push(ApiVariant {
                        docs: variant.docs,
                        index: variant.index,
                        ident: variant.ident,
                        fields: ApiFields {
                            fields: api_fields,
                            named,
                        },
                        attrs: variant.attrs,
                        value: variant.value,
                    });
                }
                ApiMembers::Variants(api_variants)
            }
        };

        match &self.kind {
            TypeKind::Object(Some(type_fn)) => {
                type_fn().collect(types);
            }
            TypeKind::Array(type_fn, _) => {
                type_fn().collect(types);
            }
            TypeKind::Tuple(type_fns) => {
                for type_fn in type_fns {
                    type_fn().collect(types);
                }
            }
            TypeKind::Vec(type_fn) => {
                type_fn().collect(types);
            }
            TypeKind::Set(type_fn) => {
                type_fn().collect(types);
            }
            TypeKind::Map(type_key_fn, type_value_fn) => {
                type_key_fn().collect(types);
                type_value_fn().collect(types);
            }
            _ => {}
        }

        let api_type_info = ApiTypeInfo {
            type_id: type_id.clone(),
            type_name: self.type_name,
            docs: self.docs,
            kind: self.kind.into(),
            ident: self.ident,
            attrs: self.attrs,
            members: api_members,
            enumer,
        };
        types.insert(type_id, api_type_info);
        for field_type_info in field_type_infos {
            field_type_info.collect(types);
        }
    }
}

fn get_constraints(
    constraits: Vec<glass::ConstraitType>,
    field_type_infos: &mut Vec<TypeInfo>,
) -> Vec<ApiConstraitType> {
    let mut result = vec![];
    for constrait in constraits {
        match constrait {
            glass::ConstraitType::Length(min, max, message) => {
                result.push(ApiConstraitType::Length(min, max, message));
            }
            glass::ConstraitType::Range(min, max, message) => {
                result.push(ApiConstraitType::Range(min, max, message));
            }
            glass::ConstraitType::Regex(regex, message) => {
                result.push(ApiConstraitType::Regex(regex, message));
            }
            glass::ConstraitType::Enumer(type_info_fn, message) => {
                let type_info = (type_info_fn)();
                result.push(ApiConstraitType::Enumer(
                    type_info.into_api_type_id(),
                    message,
                ));
                field_type_infos.push(type_info);
            }
            glass::ConstraitType::SelfValidate(message) => {
                result.push(ApiConstraitType::SelfValidate(message));
            }
            glass::ConstraitType::Constant(value, message) => {
                result.push(ApiConstraitType::Constant(value, message));
            }
        }
    }
    result
}

#[derive(Debug, Serialize, Deserialize)]
pub struct JsonValue(pub serde_json::Value);

impl TryFrom<glass::Value> for JsonValue {
    type Error = glass::ConvertError;

    fn try_from(value: glass::Value) -> Result<Self, Self::Error> {
        match value {
            glass::Value::Nil => Ok(Self(serde_json::Value::Null)),
            glass::Value::UnitStruct => Ok(Self(serde_json::Value::Object(serde_json::Map::new()))),
            glass::Value::NamedStruct(hash_map) => {
                let mut map = serde_json::Map::new();
                for (key, value) in hash_map {
                    let value: JsonValue =
                        value
                            .try_into()
                            .map_err(|_| glass::ConvertError::TypeMismatch {
                                expected: String::default(),
                                actual: String::default(),
                            })?;
                    map.insert(key, value.0);
                }
                Ok(Self(serde_json::Value::Object(map)))
            }
            glass::Value::TupleStruct(values) => {
                let mut array = vec![];
                for value in values {
                    let value: JsonValue =
                        value
                            .try_into()
                            .map_err(|_| glass::ConvertError::TypeMismatch {
                                expected: String::default(),
                                actual: String::default(),
                            })?;
                    array.push(value.0);
                }
                Ok(Self(serde_json::Value::Array(array)))
            }
            glass::Value::Enum(variant, value) => {
                todo!()
            }
            glass::Value::Bool(_) => todo!(),
            glass::Value::I8(_) => todo!(),
            glass::Value::U8(_) => todo!(),
            glass::Value::I16(_) => todo!(),
            glass::Value::U16(_) => todo!(),
            glass::Value::I32(_) => todo!(),
            glass::Value::Isize(_) => todo!(),
            glass::Value::U32(_) => todo!(),
            glass::Value::Usize(_) => todo!(),
            glass::Value::I64(_) => todo!(),
            glass::Value::U64(_) => todo!(),
            glass::Value::I128(_) => todo!(),
            glass::Value::U128(_) => todo!(),
            glass::Value::F32(_) => todo!(),
            glass::Value::F64(_) => todo!(),
            glass::Value::String(_) => todo!(),
            glass::Value::File(items) => todo!(),
            glass::Value::Option(value) => todo!(),
            glass::Value::Result(value) => todo!(),
            glass::Value::Array(values) => todo!(),
            glass::Value::Tuple(values) => todo!(),
            glass::Value::Vec(values) => todo!(),
            glass::Value::Set(values) => todo!(),
            glass::Value::Map(hash_map) => todo!(),
            glass::Value::Any(any) => todo!(),
        }
    }
}

impl TryInto<glass::Object> for JsonValue {
    type Error = glass::ConvertError;

    fn try_into(self) -> Result<glass::Object, Self::Error> {
        todo!()
    }
}

impl glass::Reflect for JsonValue {
    fn kind() -> TypeKind {
        TypeKind::Object(None)
    }
}
